package com.pboc.shiro;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.pboc.util.RequestUtil;
import com.pboc.util.ResponseUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Service;


@Service("formAuthenticationFilter")
public class FormAuthenticationCaptchaFilter extends FormAuthenticationFilter {

    /*
     * 主要是针对登入成功的处理方法。对于请求头是AJAX的之间返回JSON字符串。
     */
    @Override
    public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
                                  ServletResponse response) throws Exception {
        if (!RequestUtil.isAjax((HttpServletRequest) request)) {// 不是ajax请求
            issueSuccessRedirect(request, response);
        } else {
            Map<String, Object> outMap = new HashMap();
            outMap.put("success", "true");
            outMap.put("message", "login success");
            ResponseUtil.renderJson((HttpServletResponse) response, outMap, new String[] {});
        }
        return false;
    }

    /**
     * 主要是处理登入失败的方法
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
                                     ServletResponse response) {
        if (!isAjax(request)) {// 不是ajax请求
            setFailureAttribute(request, e);
            return true;
        }
        Map<String, Object> outMap = new HashMap();
        outMap.put("success", "false");
        outMap.put("message", e.getMessage());
        ResponseUtil.renderJson((HttpServletResponse) response, outMap, new String[] {});
        return false;
    }

    /**
     * 所有请求都会经过的方法。
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            if (isLoginRequest(request, response)) {
                if (isLoginSubmission(request, response)) {

                    return executeLogin(request, response);
                } else {
                    // allow them to see the login page ;)
                    return true;
                }
            } else {
                //saveRequestAndRedirectToLogin(request, response);
                redirectToLogin(request, response);
            }

        return false;
    }
    /**
     * 登录提交则强制返回false从而触发重新登录.
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
                                      Object mappedValue) {
        if (isLoginRequest(request, response) && isLoginSubmission(request, response)) {
            return false;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }
    private boolean isAjax(ServletRequest request) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        return "XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"));
    }

    /**
     * 覆盖父类方法、解决所有失败的认证都跳转到登录页面
     */
    protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {
        WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
    }

    /**
     * 重写执行登录方法,解决jsessionid重置的问题，排除掉Session_Fixation漏洞
     */
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
                    + "must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        }
        try {
            final Subject subject = getSubject(request, response);
            // 获取session数据
            Session session = subject.getSession();
            final LinkedHashMap<Object, Object> attributes = new LinkedHashMap<Object, Object>();
            final Collection<Object> keys = session.getAttributeKeys();
            for (Object key : keys) {
                final Object value = session.getAttribute(key);
                if (value != null) {
                    attributes.put(key, value);
                }
            }
            session.stop();
            // 登录成功后复制session数据
            session = subject.getSession(true);
            for (final Object key : attributes.keySet()) {
                session.setAttribute(key, attributes.get(key));
            }
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
    }

}